package com.jason.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Logger;

import com.jason.nio.handler.Handler;
import com.jason.nio.util.RunExecutor;


public class NioServer {
	
	
	private static final int Buffer = 1024;
	
	private static final int DefaultPort = 1984;
	
	private static final int DefaultPool = 10;
	
	private Logger log = Logger.getLogger("NioServer");
	
	private Object lock = new Object();
	
	private int port;
	
	private int pool;
	
	private IoHandler ioHandler;
	
	private RunExecutor executor;
	
	private Selector selector;
	
	private NioIoProcesser[] processers;
	
	private int processerDistributor = 0;
	
	public NioServer(){
		this(DefaultPort,DefaultPool);
	}
	
	public NioServer(int port,int pool){
		this.port = port;
		this.pool = pool;
		executor = new RunExecutor();
		processers = new NioIoProcesser[pool];
		for(int i=0;i < pool;i++){
			processers[i] = new NioIoProcesser(executor);
		}
	}
	
	public void bind(IoHandler ioHandler) throws IOException{
		this.ioHandler = ioHandler;
		synchronized(lock){
			selector = Selector.open();
			registe();
			Worker worker = new Worker();
			executor.execute(worker);
		}
		log.info("nio server is start!");
	}
	
	public void registe() throws IOException{
		ServerSocketChannel ssc = null;
        ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        ssc.socket().setReuseAddress(true);
        ssc.socket().setReceiveBufferSize(Buffer);
        ssc.socket().bind(new InetSocketAddress(this.port));
        ssc.register(selector, SelectionKey.OP_ACCEPT);
	}
	
	private NioIoProcesser getNextProcesser(){
		if(this.processerDistributor == Integer.MAX_VALUE){
			this.processerDistributor = Integer.MAX_VALUE % this.pool;
		}
		return processers[processerDistributor++ % this.pool];
	}
	
	
	private class Worker implements Runnable{
		
		public void run(){
			Selector selector = NioServer.this.selector;
			for(;;){
				try{
					int key = selector.select();
					if(key >0){
						processKey(selector.selectedKeys());
					}
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		}
		
		public void processKey(Set<SelectionKey> keys) throws IOException{
			Iterator<SelectionKey> it = keys.iterator();
			while(it.hasNext()){
				SelectionKey key = it.next();
				it.remove();
				if(!key.isAcceptable()){
					return;
				}
				ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
				SocketChannel ch = ssc.accept();
				if(ch == null){
					continue;
				}
				
				NioSession session = new NioSession(NioServer.this,getNextProcesser(),ch,NioServer.this.ioHandler);
				session.getProcesser().addNew(session);
			}
		}
	}
	
	
	
	public static void main(String[] arg){
		NioServer server = new NioServer();
		IoHandler handler = new Handler();
		try{
			server.bind(handler);
		}catch(Exception e){
			e.printStackTrace();
		}
		
	}
	
	

}
